/*
 * ResourceManager.cpp
 *
 *  Created on: Mar 24, 2010
 *      Author: dcrown
 */

#include <windows.h>
#include <fstream>
#include "ResourceManager.h"
#include "Logger.h"

const char MP_DELIM = ',';
const string MP_END = "<MP_END>";
const int MP_LINE_LENGTH = 3;
const int COMMENT_CHAR = '#';

using namespace std;

string ResourceManager::OpenFile(string filename)
{
	//Logger::getInstance().LOG(Logger::DEBUG, "OpenFile");
	ifstream* mpFile = new ifstream(filename.c_str(), std::ios::in);

	if (mpFile->is_open())
	{
		openFiles.insert(pair<string, ifstream*>(filename, mpFile));
		return filename;
	}
	else
	{
		Logger::getInstance().LOG(Logger::RECOVERABLE, "Error opening file: %s", filename.c_str());
	}
	return "";
}

void ResourceManager::CloseFile(string fileId)
{
	//Logger::getInstance().LOG(Logger::DEBUG, "CloseFile");
	map<string, ifstream*>::iterator iter = openFiles.find(fileId);
	if (iter != openFiles.end())
	{
		(iter->second)->close();
		openFiles.erase(iter);
	}
	else
	{
		Logger::getInstance().LOG(Logger::RECOVERABLE, "Invalid attempt to close an unopened file: %s", fileId.c_str());
	}
}

void ResourceManager::Destroy()
{
	map<string, ifstream*>::iterator iter = openFiles.begin();
	while (iter != openFiles.end())
	{
		(iter->second)->close();
		iter++;
	}
	openFiles.clear();
}

ifstream* ResourceManager::GetIfStream(string fileId)
{
	//Logger::getInstance().LOG(Logger::DEBUG, "GetIfStream");
	map<string, ifstream*>::iterator iter = openFiles.find(fileId);
	if (iter != openFiles.end())
	{
		return iter->second;
	}
	else
	{
		Logger::getInstance().LOG(Logger::RECOVERABLE, "Invalid attempt to get an unopened file: %s", fileId.c_str());
	}
}

int ResourceManager::ReadBaddieHeader(string fileId)
{
	string line;
	//Logger::getInstance().LOG(Logger::DEBUG, "ReadBaddieHeader");
	ifstream* baddieFile = GetIfStream(fileId);
	GetNextDataLine(baddieFile, line);
	if (IsValidInt(line))
	{
		return atoi(line.c_str());
	}

	return 0;
}

Baddie* ResourceManager::ReadBaddie(string fileId)
{
	//Logger::getInstance().LOG(Logger::DEBUG, "ReadBaddie");
	ifstream* baddieFile = GetIfStream(fileId);

	int spawnTime = -1;
	float normXPos = -1;
	float normYPos = -1;

	vector<string> tokens;
	string line;

	//line 1 is spawnTime
	GetNextDataLine(baddieFile, line);
	if (IsValidInt(line))
	{
		spawnTime = atoi(line.c_str());
		//Logger::getInstance().LOG(Logger::DEBUG, "SpawnTime = %d", spawnTime);
	}

	//line 2 is normalized x,y
	GetNextDataLine(baddieFile, line);
	tokens = SplitString(line, MP_DELIM);
	if (IsValidFloat(tokens))
	{
		//Logger::getInstance().LOG(Logger::FATAL, "norm0 %s, norm1 %s", tokens[0].c_str(), tokens[1].c_str());
		normXPos = atof(tokens[0].c_str());
		normYPos = atof(tokens[1].c_str());
	}

	MovePattern* mp = ReadMovePattern(baddieFile);

	if ((spawnTime == -1) || (normXPos == -1) || (normYPos == -1) || (mp == NULL))
	{
		return NULL;
	}
	else
	{
		Baddie* b = new Baddie;
		//Logger::getInstance().LOG(Logger::DEBUG, "SpawnTime = %d", spawnTime);
		b->Create(normXPos, normYPos, spawnTime, mp);
		return b;
	}
}

MovePattern* ResourceManager::ReadMovePattern(ifstream* mpFile)
{
	//Logger::getInstance().LOG(Logger::DEBUG, "ReadMovePattern");
	vector<int> times;
	int timeRand;
	vector<unsigned short> bearings;
	unsigned short bearingRand;
	vector<float> speeds;
	int speedRand;
	int numChanges;
	bool repeat = false;

	vector<string> tokens;
	string line;

	//line 1 is randomness
	GetNextDataLine(mpFile, line);
	tokens = SplitString(line, MP_DELIM);
	if (tokens.size() == MP_LINE_LENGTH)
	{
		if (IsValidInt(tokens[0]) && IsValidInt(tokens[1]) && IsValidFloat(tokens[2]))
		{
			timeRand = atoi(tokens[0].c_str());
			bearingRand = atoi(tokens[1].c_str());
			speedRand = atoi(tokens[2].c_str());
		}
	}

	//line 2 is repeat
	GetNextDataLine(mpFile, line);
	if ((line == "1") || (line == "true"))
	{
		repeat = true;
	}

	//line 3 is num changes
	GetNextDataLine(mpFile, line);
	if (IsValidInt(line))
	{
		numChanges = atoi(line.c_str());

		//lines 4 and on are the changes
		for (int i = 0; i < numChanges; i++)
		{
			GetNextDataLine(mpFile, line);
			tokens = SplitString(line, MP_DELIM);
			if (tokens.size() == MP_LINE_LENGTH)
			{
				if (IsValidInt(tokens[0]) && IsValidInt(tokens[1]) && IsValidFloat(tokens[2]))
				{
					times.push_back(atoi(tokens[0].c_str()));
					bearings.push_back(atoi(tokens[1].c_str()));
					speeds.push_back(atoi(tokens[2].c_str()));
				}
				else
				{
					Logger::getInstance().LOG(Logger::FATAL, "MovePattern has incorrect fornat: \"%s\"", line.c_str());
					return NULL;
				}
			}
			else
			{
				Logger::getInstance().LOG(Logger::FATAL, "Incorrect # of tokens found in MovePattern: \"%s\"", line.c_str());
				return NULL;
			}
		}
	}
	else
	{
		Logger::getInstance().LOG(Logger::FATAL, "MovePattern has incorrect fornat: \"%s\"", line.c_str());
		return NULL;
	}
	MovePattern* mp = new MovePattern;
	mp->Create(times, bearings, speeds, repeat, timeRand, bearingRand, speedRand);

	return mp;
}

//----------------------------------------------------------------------------------------------------------------------
//Utility Functions
//----------------------------------------------------------------------------------------------------------------------
vector<string> ResourceManager::SplitString(string line, char delim)
{
	vector<string> tokens;
	string token;

	int start = 0;
	int stop = line.find(delim, start);
	//Logger::getInstance().LOG(Logger::FATAL, "stop = %d",stop);
	while (stop != string::npos)
	{
		token = line.substr(start, stop-start);
		//Logger::getInstance().LOG(Logger::DEBUG, "Adding: %s", token.c_str());
		tokens.push_back(token);

		start = stop+1;
		stop = line.find(delim, start);
		//Logger::getInstance().LOG(Logger::FATAL, "stop = %d",stop);
	}

	//there must be a more elegant way to do this
	token = line.substr(start, line.size()-start);
	//Logger::getInstance().LOG(Logger::DEBUG, "Final Adding: %s", token.c_str());
	tokens.push_back(token);

	return tokens;
}

bool ResourceManager::ContainsOnlyDigits(vector<string> theStrings)
{
	vector<string>::iterator iter;
	//Logger::getInstance().LOG(Logger::FATAL, "5");
	iter = theStrings.begin();
	while (iter != theStrings.end())
	{
		//Logger::getInstance().LOG(Logger::FATAL, "%s", iter->c_str());
		for (int i = 0; i < iter->size(); i++)
		{
			//Logger::getInstance().LOG(Logger::FATAL, "char = %c, i = %d, max = %d", (*iter)[i], i, iter->size());
			if (((*iter)[i] > '9') || ((*iter)[i] < '0'))
			{
				return false;
			}
		}
		iter++;
	}

	return true;
}

bool ResourceManager::IsValidFloat(vector<string> theStrings)
{
	vector<string>::iterator iter = theStrings.begin();
	//Logger::getInstance().LOG(Logger::FATAL, "1");
	while (iter != theStrings.end())
	{
		if (!IsValidFloat(*iter))
		{
			return false;
		}
		iter++;
	}

	return true;
}

bool ResourceManager::IsValidFloat(string theString)
{
	bool dotNotSeen = true;
	bool negNotSeen = true;
	for (int i = 0; i < theString.size(); i++)
	{
		if (theString[i] == '.')
		{
			if (dotNotSeen)
			{
				dotNotSeen = false;
			}
			else
			{
				return false;
			}
		}
		else if (theString[i] == '-')
		{
			if (negNotSeen)
			{
				negNotSeen = false;
			}
			else
			{
				return false;
			}
		}
		else if ((theString[i] > '9') || (theString[i] < '0'))
		{
			return false;
		}
	}

	return true;
}

bool ResourceManager::IsValidInt(string theString)
{
	bool negNotSeen = true;
	for (int i = 0; i < theString.size(); i++)
	{
		if (theString[i] == '-')
		{
			if (negNotSeen)
			{
				negNotSeen = false;
			}
			else
			{
				return false;
			}
		}
		else if ((theString[i] > '9') || (theString[i] < '0'))
		{
			return false;
		}
	}

	return true;
}

bool ResourceManager::IsValidBool(vector<string> theStrings)
{
	vector<string>::iterator iter = theStrings.begin();

	while (iter != theStrings.end())
	{
		if (!IsValidBool(*iter))
		{
			return false;
		}
	}
}

bool ResourceManager::IsValidBool(string theString)
{
	return ((theString == "0") || (theString == "1") || (theString == "true") || (theString == "false"));
}

bool ResourceManager::StartsWith(string theString, string startingMatch)
{
	return (theString.find(startingMatch) == 0);
}

bool ResourceManager::StartsWith(string theString, char startingMatch)
{
	return (theString.find(startingMatch) == 0);
}

bool ResourceManager::ContainsOnlyWhitespace(string theString)
{
	for (int i = 0; i < theString.size(); i++)
	{
		if (theString[i] != ' ')
		{
			return false;
		}
	}

	return true;
}

void ResourceManager::GetNextDataLine(ifstream* file, string& line)
{
	do
	{
		getline(*file, line);
	} while (ContainsOnlyWhitespace(line) || StartsWith(line, COMMENT_CHAR));
	//Logger::getInstance().LOG(Logger::DEBUG, "Read: %s", line.c_str());
}
